<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Barcode
 * @subpackage Renderer
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Image.php 20366 2010-01-18 03:56:52Z ralph $
 */

/** @see Zend_Barcode_Renderer_RendererAbstract*/
require_once 'Zend/Barcode/Renderer/RendererAbstract.php';

/**
 * Class for rendering the barcode as svg
 *
 * @category   Zend
 * @package    Zend_Barcode
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Barcode_Renderer_Svg extends Zend_Barcode_Renderer_RendererAbstract {
	
	/**
	 * Resource for the image
	 * @var DOMDocument
	 */
	protected $_resource = null;
	
	/**
	 * Root element of the XML structure
	 * @var DOMElement
	 */
	protected $_rootElement = null;
	
	/**
	 * Height of the rendered image wanted by user
	 * @var integer
	 */
	protected $_userHeight = 0;
	
	/**
	 * Width of the rendered image wanted by user
	 * @var integer
	 */
	protected $_userWidth = 0;
	
	/**
	 * Set height of the result image
	 * @param null|integer $value
	 * @return Zend_Image_Barcode_Abstract
	 * @throw Zend_Image_Barcode_Exception
	 */
	public function setHeight($value) {
		if (! is_numeric ( $value ) || intval ( $value ) < 0) {
			require_once 'Zend/Barcode/Renderer/Exception.php';
			throw new Zend_Barcode_Renderer_Exception ( 'Svg height must be greater than or equals 0' );
		}
		$this->_userHeight = intval ( $value );
		return $this;
	}
	
	/**
	 * Get barcode height
	 *
	 * @return int
	 */
	public function getHeight() {
		return $this->_userHeight;
	}
	
	/**
	 * Set barcode width
	 *
	 * @param mixed $value
	 * @return void
	 */
	public function setWidth($value) {
		if (! is_numeric ( $value ) || intval ( $value ) < 0) {
			require_once 'Zend/Barcode/Renderer/Exception.php';
			throw new Zend_Barcode_Renderer_Exception ( 'Svg width must be greater than or equals 0' );
		}
		$this->_userWidth = intval ( $value );
		return $this;
	}
	
	/**
	 * Get barcode width
	 *
	 * @return int
	 */
	public function getWidth() {
		return $this->_userWidth;
	}
	
	/**
	 * Set an image resource to draw the barcode inside
	 *
	 * @param DOMDocument $value
	 * @return Zend_Barcode_Renderer
	 * @throw Zend_Barcode_Renderer_Exception
	 */
	public function setResource($svg) {
		if (! $svg instanceof DOMDocument) {
			require_once 'Zend/Barcode/Renderer/Exception.php';
			throw new Zend_Barcode_Renderer_Exception ( 'Invalid DOMDocument resource provided to setResource()' );
		}
		$this->_resource = $svg;
		return $this;
	}
	
	/**
	 * Initialize the image resource
	 *
	 * @return void
	 */
	protected function _initRenderer() {
		$barcodeWidth = $this->_barcode->getWidth ( true );
		$barcodeHeight = $this->_barcode->getHeight ( true );
		
		$backgroundColor = $this->_barcode->getBackgroundColor ();
		$imageBackgroundColor = 'rgb(' . implode ( ', ', array (($backgroundColor & 0xFF0000) >> 16, ($backgroundColor & 0x00FF00) >> 8, ($backgroundColor & 0x0000FF) ) ) . ')';
		
		$width = $barcodeWidth;
		$height = $barcodeHeight;
		if ($this->_userWidth && $this->_barcode->getType () != 'error') {
			$width = $this->_userWidth;
		}
		if ($this->_userHeight && $this->_barcode->getType () != 'error') {
			$height = $this->_userHeight;
		}
		if ($this->_resource === null) {
			$this->_resource = new DOMDocument ( '1.0', 'utf-8' );
			$this->_resource->formatOutput = true;
			$this->_rootElement = $this->_resource->createElement ( 'svg' );
			$this->_rootElement->setAttribute ( 'xmlns', "http://www.w3.org/2000/svg" );
			$this->_rootElement->setAttribute ( 'version', '1.1' );
			$this->_rootElement->setAttribute ( 'width', $width );
			$this->_rootElement->setAttribute ( 'height', $height );
			
			$this->_appendRootElement ( 'title', array (), "Barcode " . strtoupper ( $this->_barcode->getType () ) . " " . $this->_barcode->getText () );
		} else {
			$this->_readRootElement ();
			$width = $this->_rootElement->getAttribute ( 'width' );
			$height = $this->_rootElement->getAttribute ( 'height' );
		}
		$this->_adjustPosition ( $height, $width );
		
		$this->_appendRootElement ( 'rect', array ('x' => $this->_leftOffset, 'y' => $this->_topOffset, 'width' => ($this->_leftOffset + $barcodeWidth - 1), 'height' => ($this->_topOffset + $barcodeHeight - 1), 'fill' => $imageBackgroundColor ) );
	}
	
	protected function _readRootElement() {
		if ($this->_resource !== null) {
			$this->_rootElement = $this->_resource->documentElement;
		}
	}
	
	/**
	 * Append a new DOMElement to the root element
	 *
	 * @param string $tagName
	 * @param array $attributes
	 * @param string $textContent
	 */
	protected function _appendRootElement($tagName, $attributes = array(), $textContent = null) {
		$newElement = $this->_createElement ( $tagName, $attributes, $textContent );
		$this->_rootElement->appendChild ( $newElement );
	}
	
	/**
	 * Create DOMElement
	 *
	 * @param string $tagName
	 * @param array $attributes
	 * @param string $textContent
	 * @return DOMElement
	 */
	protected function _createElement($tagName, $attributes = array(), $textContent = null) {
		$element = $this->_resource->createElement ( $tagName );
		foreach ( $attributes as $k => $v ) {
			$element->setAttribute ( $k, $v );
		}
		if ($textContent !== null) {
			$element->appendChild ( new DOMText ( ( string ) $textContent ) );
		}
		return $element;
	}
	
	/**
	 * Check barcode parameters
	 *
	 * @return void
	 */
	protected function _checkParams() {
		$this->_checkDimensions ();
	}
	
	/**
	 * Check barcode dimensions
	 *
	 * @return void
	 */
	protected function _checkDimensions() {
		if ($this->_resource !== null) {
			$this->_readRootElement ();
			$height = ( float ) $this->_rootElement->getAttribute ( 'height' );
			if ($height < $this->_barcode->getHeight ( true )) {
				require_once 'Zend/Barcode/Renderer/Exception.php';
				throw new Zend_Barcode_Renderer_Exception ( 'Barcode is define outside the image (height)' );
			}
		} else {
			if ($this->_userHeight) {
				$height = $this->_barcode->getHeight ( true );
				if ($this->_userHeight < $height) {
					require_once 'Zend/Barcode/Renderer/Exception.php';
					throw new Zend_Barcode_Renderer_Exception ( sprintf ( "Barcode is define outside the image (calculated: '%d', provided: '%d')", $height, $this->_userHeight ) );
				}
			}
		}
		if ($this->_resource !== null) {
			$this->_readRootElement ();
			$width = $this->_rootElement->getAttribute ( 'width' );
			if ($width < $this->_barcode->getWidth ( true )) {
				require_once 'Zend/Barcode/Renderer/Exception.php';
				throw new Zend_Barcode_Renderer_Exception ( 'Barcode is define outside the image (width)' );
			}
		} else {
			if ($this->_userWidth) {
				$width = ( float ) $this->_barcode->getWidth ( true );
				if ($this->_userWidth < $width) {
					require_once 'Zend/Barcode/Renderer/Exception.php';
					throw new Zend_Barcode_Renderer_Exception ( sprintf ( "Barcode is define outside the image (calculated: '%d', provided: '%d')", $width, $this->_userWidth ) );
				}
			}
		}
	}
	
	/**
	 * Draw the barcode in the rendering resource
	 * @return mixed
	 */
	public function draw() {
		parent::draw ();
		$this->_resource->appendChild ( $this->_rootElement );
		return $this->_resource;
	}
	
	/**
	 * Draw and render the barcode with correct headers
	 *
	 * @return mixed
	 */
	public function render() {
		$this->draw ();
		header ( "Content-Type: image/svg+xml" );
		echo $this->_resource->saveXML ();
	}
	
	/**
	 * Draw a polygon in the svg resource
	 *
	 * @param array $points
	 * @param integer $color
	 * @param boolean $filled
	 */
	protected function _drawPolygon($points, $color, $filled = true) {
		$color = 'rgb(' . implode ( ', ', array (($color & 0xFF0000) >> 16, ($color & 0x00FF00) >> 8, ($color & 0x0000FF) ) ) . ')';
		$orientation = $this->getBarcode ()->getOrientation ();
		$newPoints = array ($points [0] [0] + $this->_leftOffset, $points [0] [1] + $this->_topOffset, $points [1] [0] + $this->_leftOffset, $points [1] [1] + $this->_topOffset, $points [2] [0] + $this->_leftOffset + cos ( - $orientation ), $points [2] [1] + $this->_topOffset - sin ( $orientation ), $points [3] [0] + $this->_leftOffset + cos ( - $orientation ), $points [3] [1] + $this->_topOffset - sin ( $orientation ) );
		$newPoints = implode ( ' ', $newPoints );
		$attributes ['points'] = $newPoints;
		$attributes ['fill'] = $color;
		$this->_appendRootElement ( 'polygon', $attributes );
	}
	
	/**
	 * Draw a polygon in the svg resource
	 *
	 * @param string $text
	 * @param float $size
	 * @param array $position
	 * @param string $font
	 * @param integer $color
	 * @param string $alignment
	 * @param float $orientation
	 */
	protected function _drawText($text, $size, $position, $font, $color, $alignment = 'center', $orientation = 0) {
		$color = 'rgb(' . implode ( ', ', array (($color & 0xFF0000) >> 16, ($color & 0x00FF00) >> 8, ($color & 0x0000FF) ) ) . ')';
		$attributes ['x'] = $position [0] + $this->_leftOffset;
		$attributes ['y'] = $position [1] + $this->_topOffset;
		//$attributes['font-family'] = $font;
		$attributes ['color'] = $color;
		$attributes ['font-size'] = $size * 1.2;
		switch ($alignment) {
			case 'left' :
				$textAnchor = 'start';
				break;
			case 'right' :
				$textAnchor = 'end';
				break;
			case 'center' :
			default :
				$textAnchor = 'middle';
		}
		$attributes ['style'] = 'text-anchor: ' . $textAnchor;
		$attributes ['transform'] = 'rotate(' . (- $orientation) . ', ' . ($position [0] + $this->_leftOffset) . ', ' . ($position [1] + $this->_topOffset) . ')';
		$this->_appendRootElement ( 'text', $attributes, $text );
	}
}
